/*
 * Copyright 2011 Clean GWT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.cleangwt.codegen;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.io.FileUtils;

import com.google.gwt.codegen.server.AbortablePrintWriter;
import com.google.gwt.codegen.server.JavaSourceWriterBuilder;
import com.google.gwt.codegen.server.SourceWriter;

/**
 * The generator context for executing a {@link CodeGenerator}.
 * 
 * @author Jess
 * @version 1.0
 * @since 1.0
 */
public class CodeGenContext {

  private static File OUTPUT_ROOT = new File("C:codegen");

  public static void main(String[] args) throws Exception {
    if (args.length == 0) {
      throw new Exception("You may have not specified a CodeGenerator class.");
    }
    String generatorClazz = args[0];
    Map<String, String> params = new HashMap<String, String>();
    for (int i = 1; i < args.length; i++) {
      if (args[i].startsWith("-")) {
        String paramName = args[i].substring(1);
        if (i == args.length - 1 || args[i + 1].startsWith("-")) {
          // add parameter
          params.put(paramName, null);
        } else {
          // add parameter with next value.
          params.put(paramName, args[++i]);
        }
      } else {
        throw new Exception("Yoy may have not specified a parameter with '-' prefixed");
      }
    }

    // prepare output dir
    if (!OUTPUT_ROOT.exists()) {
      FileUtils.forceMkdir(OUTPUT_ROOT);
    }

    System.out.println(generatorClazz + "is running");

    CodeGenContext ctx = new CodeGenContext(generatorClazz, params);
    CodeGenerator cg = (CodeGenerator) Class.forName(generatorClazz).newInstance();
    cg.run(ctx);

    System.out.println(generatorClazz + " finished.");
    System.out.println("#Check out the file at "
        + new File(OUTPUT_ROOT, ctx.getTargetFolder()).getPath());
  }

  private final Map<String, String> params;
  private final String targetGenerator;

  /**
   * Creates a code generator context for the specified {@link CodeGenerator} class name with
   * parameters.
   * 
   * @param generatorClazz the {@link CodeGenerator} class name
   * @param params the parameters
   */
  public CodeGenContext(String generatorClazz, Map<String, String> params) {
    this.targetGenerator = generatorClazz;
    this.params = params;
  }

  /**
   * 
   * @param fileName
   * @return
   * @throws IOException
   */
  public PrintWriter createResourceWriter(String fileName) throws IOException {
    return this.createResourceWriter(this.getTargetFolder(), fileName);
  }

  /**
   * Creates a resource writer with the given path and file name.
   * 
   * @param path
   * @param fileName
   * @return
   * @throws IOException
   */
  public PrintWriter createResourceWriter(String path, String fileName) throws IOException {
    File package_dir = new File(OUTPUT_ROOT, path);

    // ensure package folder
    if (!package_dir.exists()) {
      FileUtils.forceMkdir(package_dir);
    }
    File f = new File(package_dir, fileName);
    return new PrintWriter(new FileOutputStream(f));
  }

  /**
   * Creates a java source for the given class name.
   * 
   * @param className the class name
   * @return
   * @throws IOException
   */
  public SourceWriter createSourceWriter(String className) throws IOException {
    return createSourceWriter(this.getTargetPackage(), className);
  }

  /**
   * Creates a java source with the given package and class name.
   * 
   * @param packageName the package name
   * @param className the class name
   * @return
   * @throws IOException
   */
  public SourceWriter createSourceWriter(String packageName, String className) throws IOException {
    return createSourceWriterBuilder(packageName, className).createSourceWriter();
  }

  /**
   * Creates a {@link JavaSourceWriterBuilder} with the given class name.
   * 
   * @param className the class name.
   * @return
   * @throws IOException
   */
  public JavaSourceWriterBuilder createSourceWriterBuilder(String className) throws IOException {
    return this.createSourceWriterBuilder(this.getTargetPackage(), className);
  }

  /**
   * Creates a {@link JavaSourceWriterBuilder} with the given package name and class name.
   * 
   * @param packageName the package name
   * @param className the class name
   * @return
   * @throws IOException
   */
  public JavaSourceWriterBuilder createSourceWriterBuilder(String packageName, String className)
      throws IOException {
    File package_dir = new File(OUTPUT_ROOT, packageName.replace(".", "/"));

    // ensure package folder
    if (!package_dir.exists()) {
      FileUtils.forceMkdir(package_dir);
    }
    File f = new File(package_dir, className + ".java");
    PrintWriter pw = new PrintWriter(new FileOutputStream(f));
    return new JavaSourceWriterBuilder(new AbortablePrintWriter(pw), packageName, className);
  }

  /**
   * Retrieves the parameter for the given key.
   * 
   * @param key the key
   * @return the parameter for the given key.
   */
  public String getParam(String key) {
    return params.get(key);
  }

  /**
   * Retrieves the target folder.
   * 
   * @return the target folder.
   */
  public String getTargetFolder() {
    return targetGenerator.toLowerCase().replace(".", "/");
  }

  /**
   * Retrieves the target package.
   * 
   * @return the target package.
   */
  public String getTargetPackage() {
    return targetGenerator.toLowerCase();
  }

  /**
   * Checks whether the parameters exists.
   * 
   * @param key the parameter key
   * @return true for exist, false for not
   */
  public boolean hasParam(String key) {
    return params.containsKey(key);
  }

}
